package com.air.repo.jdk8;

/**
 * 来自网络 https://www.cnblogs.com/xll1025/p/6486170.html
 * 一个简单的展示Happen-Before的例子.
 * 这里有两个共享变量:a和flag,初始值分别为0和false.在ThreadA中先给a=1,然后flag=true.
 * 如果按照有序的话,那么在ThreadB中如果if(flag)成功的话,则应该a=1,而a=a*1之后a仍然为1,下方的if(a==0)应该永远不会为真,永远不会打印.
 * 但实际情况是:在试验100次的情况下会出现0次或几次的打印结果,而试验1000次结果更明显,有十几次打印.
 */
public class SimpleHappenBefore {
    /** 这是一个验证结果的变量 */
    private static int a=0;
    /** 这是一个标志位 */
    private static boolean flag=false;
 
    public static void main(String[] args) throws InterruptedException {

        int releases=0;
        assert releases == 1;
        //由于多线程情况下未必会试出重排序的结论,所以多试一些次
        for(int i=0;i<1000;i++){
            ThreadA threadA=new ThreadA();
            ThreadB threadB=new ThreadB();
            threadA.start();
            threadB.start();
 
            //这里等待线程结束后,重置共享变量,以使验证结果的工作变得简单些.
            threadA.join();
            threadB.join();
            a=0;
            flag=false;
        }
    }
 
    static class ThreadA extends Thread{
        public void run(){
            a=1;
            flag=true;
        }
    }
 
    static class ThreadB extends Thread{
        public void run(){
            if(flag){
                a=a*1;
            }
            if(a==0){
                System.out.println("ha,a==0");
            }
        }
    }
}

/**
 在虚拟机层面，为了尽可能减少内存操作速度远慢于CPU运行速度所带来的CPU空置的影响，
 虚拟机会按照自己的一些规则(这规则后面再叙述)将程序编写顺序打乱——即写在后面的代码在时间顺序上可能会先执行，
 而写在前面的代码会后执行——以尽可能充分地利用CPU。拿上面的例子来说：假如不是a=1的操作，
 而是a=new byte[1024*1024](分配1M空间)，那么它会运行地很慢，此时CPU是等待其执行结束呢，
 还是先执行下面那句flag=true呢？显然，先执行flag=true可以提前使用CPU，加快整体效率，
 当然这样的前提是不会产生错误(什么样的错误后面再说)。虽然这里有两种情况：后面的代码先于前面的代码开始执行；
 前面的代码先开始执行，但当效率较慢的时候，后面的代码开始执行并先于前面的代码执行结束。
 不管谁先开始，总之后面的代码在一些情况下存在先结束的可能。
 */